package punctuatedEquilibrium;

import java.util.LinkedList;
import java.util.List;

public class Population {

	private List<Individual>		individuals;
	private Phenotype				lastGenerationsAverageTraits;
	private Phenotype				optimalTraits;
	private int						populationSize;
	private IndividualParameters	individualParameters;

	public Population(SimulationParameters parameters) {
		individualParameters = new IndividualParameters(parameters);
		populationSize = parameters.populationSize();
		initializeIndividuals();
	}

	public Phenotype reportAverageTraits() {
		return lastGenerationsAverageTraits;
	}

	public void simulateOneGeneration(Phenotype optimalTraits) {
		this.optimalTraits = optimalTraits;
		letIndividualsTryToProduce();
		calculateAverageTraitsOfSurvivors();
		regenerateBasedOnSurvivingTraits();
	}

	private void addNewIndividualBasedOnAverageTraitsAndVariability() {
		Individual newIndividual = newIndividualWithTraitsVaryingRandomlyFromAverageTraits();
		individuals.add(newIndividual);
	}

	private void calculateAverageTraitsOfSurvivors() {
		SetOfPhenotypes traitsOfThePopulation = new SetOfPhenotypes();
		for (Individual individual : individuals) {
			if (individual.isAlive()) {
				traitsOfThePopulation.add(individual.reportTraits());
			}
		}
		lastGenerationsAverageTraits = traitsOfThePopulation
				.computeAveragePhenotype();
	}

	private void initializeIndividuals() {
		individuals = new LinkedList<Individual>();
		for (int i = 0; i < populationSize; i++) {
			individuals.add(IndividualFactory.getStandardIndividual());
		}
	}

	private void letIndividualsDetermineFitnessBasedOnOptimalTraits() {
		for (Individual individual : individuals) {
			individual.evaluateFitnessBasedOn(optimalTraits,
					lastGenerationsAverageTraits);
		}
	}

	private void letIndividualsTryToProduce() {
		for (Individual individual : individuals) {
			individual.tryToReproduce();
		}
	}

	private Individual newIndividualWithTraitsVaryingRandomlyFromAverageTraits() {
		Phenotype newTraits = lastGenerationsAverageTraits
				.duplicateApplyingSmallVariability();
		Individual newIndividual = new Individual(newTraits,
				individualParameters);
		return newIndividual;
	}

	private void refillPopulationBasedOnAverageTraitsAndVariability() {
		for (int i = 0; i < populationSize; i++) {
			addNewIndividualBasedOnAverageTraitsAndVariability();
		}
	}

	private void regenerateBasedOnSurvivingTraits() {
		removeAllIndividualsFromPopulation();
		refillPopulationBasedOnAverageTraitsAndVariability();
		letIndividualsDetermineFitnessBasedOnOptimalTraits();
	}

	private void removeAllIndividualsFromPopulation() {
		individuals.clear();
	}
}
